class Shape {
  constructor(selector) {
    // 获取canvas元素
    this.canvas = document.querySelector(selector);
    // 设置 canvas 的宽高
    this.canvas.width = window.innerWidth;
    this.canvas.height = window.innerHeight;
    // 获取webgl上下文
    this.gl = this.canvas.getContext("webgl");
    // 初始化着色器
    this.initShader();
    // 链接程序
    this.initProgram();
    // 绘制
    this.initDraw();
  }
  initShader() {
    // 创建顶点着色器
    this.vShader = this.gl.createShader(this.gl.VERTEX_SHADER);
    // 顶点着色器代码
    this.gl.shaderSource(
      this.vShader,
      /* glsl*/ `
        attribute vec4 a_position;  // 顶点位置属性
        attribute vec4 a_color; // 顶点颜色属性
        attribute vec2 uv; // 顶点纹理坐标属性
        varying vec4 v_color; // 声明varying变量
        varying vec2 v_uv; // 声明varying变量
        void main() {
          gl_Position = a_position;
          gl_PointSize = 20.0;
          v_color = a_color; // 将顶点颜色传递给varying变量
          v_uv = uv; // 将顶点纹理坐标传递给varying变量
        }
      `
    );
    // 编译顶点着色器
    this.gl.compileShader(this.vShader);

    // 创建片元着色器
    this.fShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
    // 片元着色器代码
    this.gl.shaderSource(
      this.fShader,
      /* glsl*/ `
        precision mediump float; // 设置精度
        varying vec4 v_color; // 接收顶点着色器传递的varying变量
        varying vec2 v_uv; // 接收顶点着色器传递的varying变量
        uniform sampler2D u_texture; // 纹理采样器
        void main() {
          // gl_FragColor = v_color; // 设置片元颜色
          // gl_FragColor = vec4(v_uv, 0.0, 1.0); // 使用纹理坐标作为颜色
          gl_FragColor = texture2D(u_texture, v_uv); // 使用纹理采样器获取颜色
        }
      `
    );
    // 编译片元着色器
    this.gl.compileShader(this.fShader);
  }
  initProgram() {
    // 创建程序对象
    this.program = this.gl.createProgram();
    // 将顶点着色器和片元着色器关联到程序对象中
    this.gl.attachShader(this.program, this.vShader);
    this.gl.attachShader(this.program, this.fShader);
    // 链接程序对象
    this.gl.linkProgram(this.program);
    // 使用程序对象
    this.gl.useProgram(this.program);
  }
  initDraw() {
    // 创建顶点数据
    this.position = this.gl.getAttribLocation(this.program, "a_position");
    // 创建数据缓冲区
    this.buffer = this.gl.createBuffer();
    // 绑定缓冲区
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);

    // 将顶点数据提供给attribute变量
    this.gl.vertexAttribPointer(
      this.position, //告诉attribute变量从缓冲区中获取数据
      2, // 每次迭代提供2个单位的数据
      this.gl.FLOAT, //数据类型
      false, //是否进行归一化
      0, // 步长，每次迭代需要获取多少个单位数据
      0 // 偏移量，从缓冲区中的第几个单位数据开始取数据
    );
    // 启用顶点属性
    this.gl.enableVertexAttribArray(this.position);

    this.vertices = [];

    // // 获取片元着色器中的uniform变量
    // this.uColor = this.gl.getUniformLocation(this.program, "u_color");
    // // 设置默认颜色
    // this.gl.uniform4f(this.uColor, 1.0, 1.0, 0.0, 1.0); // 黄色

    // 获取顶点着色器中的attribute变量
    this.aColor = this.gl.getAttribLocation(this.program, "a_color");
    // 创建颜色缓冲区
    this.colorBuffer = this.gl.createBuffer();
    // 绑定颜色缓冲区
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorBuffer);
    // 将颜色数据提供给attribute变量
    this.gl.vertexAttribPointer(
      this.aColor, // 告诉attribute变量从缓冲区中获取数据
      4, // 每次迭代提供4个单位的数据（RGBA）
      this.gl.FLOAT, // 数据类型
      false, // 是否进行归一化
      0, // 步长，每次迭代需要获取多少个单位数据
      0 // 偏移量，从缓冲区中的第几个单位数据开始取数据
    );
    // 启用颜色属性
    this.gl.enableVertexAttribArray(this.aColor);
    // 初始化顶点颜色数据
    this.colors = [
      // 黑色
      0.0,
      0.0,
      0.0,
      1.0, // 第一个顶点颜色
      // 红色
      1.0,
      0.0,
      0.0,
      1.0, // 第二个顶点颜色
      // 黄色
      1.0,
      1.0,
      0.0,
      1.0, // 第三个顶点颜色
      // 绿色
      0.0,
      1.0,
      0.0,
      1.0, // 第四个顶点颜色
    ];
    // 设置颜色缓冲区数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.colors),
      this.gl.STATIC_DRAW // 静态绘制
    );
  }
  drawPoint(x, y) {
    // 设置点的颜色
    this.colors = [1.0, 0.0, 1.0, 1.0]; // 紫色
    // 绑定颜色缓冲区
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorBuffer);
    // 设置颜色数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.colors),
      this.gl.STATIC_DRAW // 静态绘制
    );

    this.vertices = [x, y]; // 创建一个点
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); // 绑定顶点缓冲区
    // 设置顶点数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.vertices),
      this.gl.STATIC_DRAW // 静态绘制
    );
    // 绘制
    this.gl.drawArrays(this.gl.POINTS, 0, 1);
  }
  moveTo(x, y) {
    this.vertices.push(x, y);
    // 设置顶点数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.vertices),
      this.gl.STATIC_DRAW // 静态绘制
    );
    this.gl.drawArrays(this.gl.POINTS, 0, this.vertices.length / 2);
  }
  lineTo(x, y) {
    this.vertices.push(x, y);
    // 设置顶点数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.vertices),
      this.gl.STATIC_DRAW // 静态绘制
    );
    this.gl.drawArrays(this.gl.POINTS, 0, this.vertices.length / 2);
    this.gl.drawArrays(this.gl.LINE_STRIP, 0, this.vertices.length / 2);
  }
  rect(x, y, width, height, color) {
    // 设置顶点的纹理坐标uv
    this.uv = this.gl.getAttribLocation(this.program, "uv");
    // 创建纹理坐标缓冲区
    this.uvBuffer = this.gl.createBuffer();
    // 绑定纹理坐标缓冲区
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.uvBuffer);
    // 设置纹理坐标数据
    this.gl.vertexAttribPointer(
      this.uv, // 告诉attribute变量从缓冲区中获取数据
      2, // 每次迭代提供2个单位的数据（u, v）
      this.gl.FLOAT, // 数据类型  默认为false
      false, // 是否进行归一化
      0, // 步长，每次迭代需要获取多少个单位数据
      0 // 偏移量，从缓冲区中的第几个单位数据开始取数据
    );
    // 启用纹理坐标属性
    this.gl.enableVertexAttribArray(this.uv);
    // 设置纹理坐标数据
    this.uvs = [
      0.0,
      0.0, // 第一个顶点纹理坐标
      1.0,
      0.0, // 第二个顶点纹理坐标
      1.0,
      1.0, // 第三个顶点纹理坐标
      0.0,
      1.0, // 第四个顶点纹理坐标
    ];
    // 设置纹理坐标缓冲区数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.uvs),
      this.gl.STATIC_DRAW // 静态绘制
    );

    // 设置顶点颜色数据
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.colorBuffer); // 绑定颜色缓冲区
    // 设置矩形颜色
    this.colors = [
      // 黑色
      0.0,
      0.0,
      0.0,
      1.0, // 第一个顶点颜色
      // 红色
      1.0,
      0.0,
      0.0,
      1.0, // 第二个顶点颜色
      // 黄色
      1.0,
      1.0,
      0.0,
      1.0, // 第三个顶点颜色
      // 绿色
      0.0,
      1.0,
      0.0,
      1.0, // 第四个顶点颜色
    ];
    // 设置颜色缓冲区数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.colors),
      this.gl.STATIC_DRAW // 静态绘制
    );

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); // 绑定顶点缓冲区
    this.vertices = [
      x,
      y, // 第0个点
      x + width,
      y, // 第1个点
      x + width,
      y + height, // 第2个点
      x,
      y + height, // 第3个点
    ];
    // 设置顶点数据
    this.gl.bufferData(
      this.gl.ARRAY_BUFFER,
      new Float32Array(this.vertices),
      this.gl.STATIC_DRAW // 静态绘制
    );
    // 创建索引数据
    this.indexBuffer = this.gl.createBuffer();
    // 绑定索引缓冲区
    this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
    // 2个三角形绘制矩形
    // 设置索引缓冲区数据
    // this.gl.bufferData(
    //   this.gl.ELEMENT_ARRAY_BUFFER,
    //   new Uint16Array([0, 1, 3, 3, 1, 2]),
    //   this.gl.STATIC_DRAW
    // );
    // 绘制
    // this.gl.drawElements(this.gl.TRIANGLES, 6, this.gl.UNSIGNED_SHORT, 0);
    // 1个三角带绘制矩形
    this.gl.bufferData(
      this.gl.ELEMENT_ARRAY_BUFFER,
      new Uint16Array([0, 1, 3, 2]),
      this.gl.STATIC_DRAW
    );
    // 绘制
    // this.gl.drawElements(this.gl.TRIANGLE_STRIP, 4, this.gl.UNSIGNED_SHORT, 0);
    this.setTexture("/imgs/ji.webp");
  }
  setTexture(imageSrc) {
    // 创建纹理对象
    this.texture = this.gl.createTexture();
    // 绑定纹理对象
    this.gl.bindTexture(this.gl.TEXTURE_2D, this.texture);
    // 设置纹理参数
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_WRAP_S,
      this.gl.CLAMP_TO_EDGE
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_WRAP_T,
      this.gl.CLAMP_TO_EDGE
    );
    this.gl.texParameteri(
      this.gl.TEXTURE_2D,
      this.gl.TEXTURE_MIN_FILTER,
      this.gl.LINEAR
    );
    // 创建图像对象
    const image = new Image();
    image.src = imageSrc;
    image.onload = () => {
      this.gl.pixelStorei(
        this.gl.UNPACK_FLIP_Y_WEBGL,
        true // 翻转Y轴
      );
      // 当图像加载完成后，将图像数据上传到纹理对象
      this.gl.texImage2D(
        this.gl.TEXTURE_2D,
        0,
        this.gl.RGBA,
        this.gl.RGBA,
        this.gl.UNSIGNED_BYTE,
        image
      );
      // 解绑纹理对象
      // this.gl.bindTexture(this.gl.TEXTURE_2D, null);
      // 绘制
      this.gl.drawElements(
        this.gl.TRIANGLE_STRIP,
        4,
        this.gl.UNSIGNED_SHORT,
        0
      );
    };
  }
}
